package com.example.springbootdemo.utils;

import com.alibaba.fastjson.JSON;
import com.example.springbootdemo.entity.Man;
import com.example.springbootdemo.entity.Weman;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 对象转换工具类
 *
 * @author xuwangqi on 2021/5/18
 */
public class BeanConvertUtil {
    /** 100个对象的转换时间，亲测
     * cglib:286
     * spring:879
     * fastjson object2map:138
     * reflet object2map:1
     */


    /**
     * ===================================================================cglib 性能较高=====================================================================
     **/
    public static Map<String, BeanCopier> beanCopierMap = new HashMap<String, BeanCopier>();

    /**
     * 单个对象属性拷贝 cglib的BeanCopier实现 性能好
     *
     * @param source 源对象
     * @param clazz  目标对象Class
     * @param <T>    目标对象类型
     * @param <M>    源对象类型
     * @return 目标对象
     */
    public static <T, M> T copyProperties(M source, Class<T> clazz) {
        if (Objects.isNull(source) || Objects.isNull(clazz)) {
            throw new IllegalArgumentException();
        }
        return copyProperties(source, clazz, null);
    }

    /**
     * 列表对象拷贝 cglib的BeanCopier实现
     *
     * @param sources 源列表
     * @param clazz   源列表对象Class
     * @param <T>     目标列表对象类型
     * @param <M>     源列表对象类型
     * @return 目标列表
     */
    public static <T, M> List<T> copyObjects(List<M> sources, Class<T> clazz) {
        if (Objects.isNull(sources) || Objects.isNull(clazz) || sources.isEmpty()) {
            throw new IllegalArgumentException();
        }
        return Optional.of(sources)
                .orElse(new ArrayList<>())
                .stream().map(m -> copyProperties(m, clazz))
                .collect(Collectors.toList());
    }

    private static String generateKey(Class<?> class1, Class<?> class2) {
        return class1.toString() + class2.toString();
    }

    /**
     * 单个对象属性拷贝 cglib的BeanCopier实现
     *
     * @param source 源对象
     * @param clazz  目标对象Class
     * @param <T>    目标对象类型
     * @param <M>    源对象类型
     * @return 目标对象
     */
    private static <T, M> T copyProperties(M source, Class<T> clazz, Converter converter) {
        BeanCopier copier = null;
        String beanKey = generateKey(source.getClass(), clazz);
        if (!beanCopierMap.containsKey(beanKey)) {
            //缓存中没有该copier对象,新建copier，并放入缓存
            copier = BeanCopier.create(source.getClass(), clazz, false);
            beanCopierMap.put(beanKey, copier);
        } else {
            copier = beanCopierMap.get(beanKey);
        }
        T t = null;
        try {
            t = clazz.newInstance();
            copier.copy(source, t, converter);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }


    /**===================================================================spring 浅拷贝=====================================================================**/


    /**
     * 方法说明：对象转换 Spring的BeanUtils实现
     *  
     *
     * @param source           原对象
     * @param target           目标对象
     * @param ignoreProperties 排除要copy的属性
     * @return 转换后的目标对象
     */
    public static <T> T copy(Object source, Class<T> target, String... ignoreProperties) {
        T targetInstance = null;
        try {
            targetInstance = target.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (ArrayUtils.isEmpty(ignoreProperties)) {
            BeanUtils.copyProperties(source, Objects.requireNonNull(targetInstance));
        } else {
            BeanUtils.copyProperties(source, Objects.requireNonNull(targetInstance), ignoreProperties);
        }
        return targetInstance;
    }

    /**
     * 方法说明：对象转换(List) Spring的BeanUtils实现
     *  
     *
     * @param list             原对象
     * @param target           目标对象
     * @param ignoreProperties 排除要copy的属性
     * @return 转换后的list对象
     */
    public static <T, E> List<T> copyList(List<E> list, Class<T> target, String... ignoreProperties) {
        List<T> targetList = new ArrayList<>();
        if (CollectionUtils.isEmpty(list)) {
            return targetList;
        }
        for (E e : list) {
            targetList.add(copy(e, target, ignoreProperties));
        }
        return targetList;
    }

    /**
     * ===================================================================map->object,object->map=====================================================================**/
    /**==============================================反射====================================================================**/
    public static Map<String,Object> obj2MapReflect(Object obj) throws Exception{
        Map<String,Object> map=new HashMap<String, Object>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for(Field field:fields){
            field.setAccessible(true);
            map.put(field.getName(), field.get(obj));
        }
        return map;
    }
    public static Object map2ObjReflect(Map<String,Object> map,Class<?> clz) throws Exception{
        Object obj = clz.newInstance();
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for(Field field:declaredFields){
            int mod = field.getModifiers();
            if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
                continue;
            }
            field.setAccessible(true);
            field.set(obj, map.get(field.getName()));
        }
        return obj;
    }
    /** ================================================================fastJson,先变成json========================================================================**/

    public static Object map2ObjectFastJson(Map<String, Object> map, Class<?> clazz) {
        if (map == null) {
            return null;
        }
        return JSON.parseObject(JSON.toJSONString(map), clazz);
    }

    /**
     * 方法说明：对象转化为Map fastjson方式
     *
     * @param object 对象
     * @return map
     */
    public static Map<String, Object> objectToMapFastJson(Object object) {
        return convertBean(object, Map.class);
    }

    /**
     * 方法说明：将bean转化为另一种bean实体 fastjson方式
     *  
     *
     * @param object      要转换的对象
     * @param entityClass 目标类型类对象
     * @return 转换后的目标对象
     */
    public static <T> T convertBean(Object object, Class<T> entityClass) {
        if (null == object) {
            return null;
        }
        return JSON.parseObject(JSON.toJSONString(object), entityClass);
    }




    public static void maintest(String[] args) {
        ArrayList<Man> men = new ArrayList<>(100);
        for (int i=0;i<100;i++){
            Man man = new Man();
            man.setName("虚妄其");
            man.setAge(24);
            man.setBasketball("打篮球");
            men.add(man);
        }
        long start = System.currentTimeMillis();
        men.forEach(man -> {
             BeanConvertUtil.copyProperties(man, Weman.class);
        });
        long end=System.currentTimeMillis();
        System.out.println("c:"+(end-start));

        start = System.currentTimeMillis();
        men.forEach(man -> {
            BeanConvertUtil.copy(man, Weman.class);
        });
       end=System.currentTimeMillis();
       System.out.println("spring:"+(end-start));


        start = System.currentTimeMillis();
        men.forEach(man -> {
            BeanConvertUtil.objectToMapFastJson(man);
        });
        end=System.currentTimeMillis();
        System.out.println("fastjson object2map:"+(end-start));

        start = System.currentTimeMillis();
        men.forEach(man -> {
            try {
                BeanConvertUtil.obj2MapReflect(man);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        end=System.currentTimeMillis();
        System.out.println("reflet object2map:"+(end-start));




    }

}

